/*
============================================================================
 Name        : dd_fixed_queue.c
 Author      : Jian
 Version     :
 Copyright   : GPL
 Description : dd_fixed_queue.c - source file
============================================================================
*/

/* INCLUDE FILES */
#include "dd_fixed_queue.h"
#include "dd_log.h"

#define __index(q, i)  (((i) < (q)->maxCount) ? (i) : (i) % (q)->maxCount)
#define __data(q, i)   ((q)->data[__index(q,i)])


dd_fixed_queue* dd_fqueue_create(DD_UINT maxCount, int supConc) 
{
    dd_fixed_queue* queue = NULL ;
    
    __DD_ASSERT_DEFENSE(maxCount > 0, return NULL) ;

    queue = dd_malloc(sizeof(dd_fixed_queue)) ;

    __DD_CHECK_MEM(queue, sizeof(dd_fixed_queue), return NULL) ;
    
    queue->maxCount = maxCount ;
    queue->count    = 0 ;
    queue->data     = (dd_queue_value**)dd_malloc(sizeof(void*) * maxCount) ;

    __DD_CHECK_MEM(queue->data, sizeof(void*) * maxCount, return NULL) ;

    if (supConc) 
        queue->lock = sd_create_sem(1) ;

    return queue ;
}

void dd_fqueue_destroy(dd_fixed_queue* queue, void (*free_fun)(void*)) 
{
    __DD_ASSERT_DEFENSE(queue, return) ;
    
    dd_fqueue_reset(queue, free_fun) ;
    sd_destroy_sem(queue->lock) ;
    dd_free(queue->data) ;
    dd_free(queue) ;
}

int dd_fqueue_enqueue(dd_fixed_queue* queue, dd_queue_value* value) 
{
    int ret = -1 ;

    if (queue->lock)  dd_wait_sem(queue->lock, return -1) ;
    
    if (queue->count < queue->maxCount) {
        __data(queue, queue->head + queue->count) = value ;
        queue->count++ ;
        ret = 0 ;
    }

    if (queue->lock)  dd_post_sem(queue->lock, ;) ;

    return ret ;
}

dd_queue_value* dd_fqueue_dequeue(dd_fixed_queue* queue) 
{
    dd_queue_value* val = NULL ;

    if (queue->lock)  dd_wait_sem(queue->lock, return NULL) ;
    
    if (queue->count > 0) {
        val = __data(queue, queue->head) ;
        __data(queue, queue->head) = NULL ;

        queue->head = __index(queue, queue->head + 1) ;
        queue->count-- ;
    }

    if (queue->lock)  dd_post_sem(queue->lock, ;) ;

    return val ;
}

int dd_fqueue_try_dequeue(dd_fixed_queue* queue, dd_fqueue_handle_value* handler) 
{
    int ret = 0;

    dd_queue_value* val = NULL ;

    if (queue->lock)  dd_wait_sem(queue->lock, return 0) ;

    if (queue->count > 0) {
        val = __data(queue, queue->head) ;
        
        if ((ret = handler(val)) != 0) {
            __data(queue, queue->head) = NULL ;

            queue->head = __index(queue, queue->head + 1) ;
            queue->count-- ;
        }
    }

    if (queue->lock)  dd_post_sem(queue->lock, ;) ;

    return ret ;
}

void dd_fqueue_reset(dd_fixed_queue* queue, void (*free_fun)(void*)) 
{
    int i = 0 ;

    if (queue->lock)  dd_wait_sem(queue->lock, return) ;

    for (; i < queue->count; i++) {
        if (free_fun)
            free_fun(__data(queue, queue->head + i)) ;
        __data(queue, queue->head + i) = NULL ;
    }
    
    queue->head  = 0 ;
    queue->count = 0 ;

    if (queue->lock)  dd_post_sem(queue->lock, return) ;
}

DD_BOOL dd_fqueue_count(const dd_fixed_queue* queue) 
{
    return queue->count ;
}


#ifdef __DD_TEST

static int _do_test(const int maxCount) 
{
    int loopCount = 0 ;
    int i = 0 ;
    
    dd_fixed_queue* queue = dd_fqueue_create(maxCount, maxCount % 2) ;

    loopCount = maxCount / 2 ;
    if (loopCount == 0)
        loopCount = 1 ;

    for (i = 0; i < loopCount; i++) {
        if (dd_fqueue_enqueue(queue, (void*)i) < 0) {
            dd_log_error("error for enqueue test, add queue failed, maxCount: %d", maxCount) ;
            return 0 ;
        }
    }

    if (dd_fqueue_count(queue) != loopCount) {
        dd_log_error("error for enqueue count test, maxCount: %d", maxCount) ;
        return 0 ;
    }

    for (i = 0; i < loopCount; i++) {
        if ((int)dd_fqueue_dequeue(queue) != i) {
            dd_log_error("error for dequeue value test , maxCount: %d", maxCount) ;
            return 0 ;
        }
    }

    if (dd_fqueue_count(queue) != 0) {
        dd_log_error("error for enqueue count test, maxCount: %d", maxCount) ;
        return 0 ;
    }

    if (dd_fqueue_dequeue(queue) != NULL) {
        dd_log_error("error for enqueue get with empty test, maxCount: %d", maxCount) ;
        return 0 ;
    }

    for (i = 0; i < maxCount; i++) {
        if (dd_fqueue_enqueue(queue, (void*)i) < 0) {
            dd_log_error("error for enqueue test, add queue to max failed, maxCount: %d", maxCount) ;
            return 0 ;
        }
    }

    if (dd_fqueue_enqueue(queue, (void*)i) >= 0) {
        dd_log_error("error for add data to max queue test failed, maxCount: %d", maxCount) ;
        return 0 ;
    }

    for (i = 0; i < maxCount; i++) {
        if ((int)dd_fqueue_dequeue(queue) != i) {
            dd_log_error("error for dequeue value test again , maxCount: %d", maxCount) ;
            return 0 ;
        }
    }

    if (dd_fqueue_count(queue) != 0) {
        dd_log_error("error for enqueue count test again, maxCount: %d", maxCount) ;
        return 0 ;
    }

    if (dd_fqueue_dequeue(queue) != NULL) {
        dd_log_error("error for enqueue get with empty test again, maxCount: %d", maxCount) ;
        return 0 ;
    }
    
    return 1 ;
}

#ifdef __SYMBIAN32__
#define __TEST_TIME  100
#define __TEST_COUNT 1000
#else
#define __TEST_TIME  1000
#define __TEST_COUNT 10000
#endif

int dd_fqueue_test() 
{
    int i = 0 ;

    srand(time(NULL)) ;

    if (!_do_test(1)) 
        return 0 ;

    if (!_do_test(20))
        return 0 ;

    if (!_do_test(999))
        return 0 ;

    for (; i < __TEST_TIME; i++) {
        if (!_do_test((rand() % __TEST_COUNT) + 1))
            return 0 ;
    }

    return 1 ;
}

#endif

/* End of File */
